<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="cjcJi" id="cjcJi"><span data-lake-id="u72c2e3fb" id="u72c2e3fb">典型回答</span></h1>
  <h3 data-lake-id="s3n4x" id="s3n4x"><span data-lake-id="u47b21bcb" id="u47b21bcb">标记-清除</span></h3>
  <p data-lake-id="ud8231953" id="ud8231953"><span data-lake-id="u1c1bc683" id="u1c1bc683">标记清除是最简单和干脆的一种垃圾回收算法，他的执行流程是这样子的：当 JVM 识别出内存中的垃圾以后，直接将其清除，但是这样有一个很明显的缺点，就是会导致内存空间的不连续，也就是会产生很多的内存碎片。先画个图来看下</span></p>
  <p data-lake-id="uab465961" id="uab465961"><span data-lake-id="u48fd8296" id="u48fd8296">​</span><br></p>
  <p data-lake-id="ud6911c32" id="ud6911c32"><img src="https://cdn.nlark.com/yuque/0/2022/png/719664/1670154913257-cc81dc24-73c5-4b4f-9eac-9ee3023b1146.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_28%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u843f1247" id="u843f1247"><span data-lake-id="u4e8e888c" id="u4e8e888c">我们使用上图左边的图来表示垃圾回收之前的样子，黑色的区域表示可以被回收的垃圾对象。这些对象在内存空间中不是连续的。右侧这张图表示是垃圾回收过后的内存的样子。可以很明显的看到里面产生了断断续续的 内存碎片。</span></p>
  <p data-lake-id="u18cc4477" id="u18cc4477"><span data-lake-id="u697a176f" id="u697a176f">​</span><br></p>
  <blockquote data-lake-id="uf4057e41" id="uf4057e41">
   <p data-lake-id="u32cba9dd" id="u32cba9dd"><span data-lake-id="u6536dabf" id="u6536dabf">那说半天垃圾不是已经被回收了吗？内存碎片就内存碎片呗。又能咋地？</span></p>
  </blockquote>
  <p data-lake-id="u5a608d38" id="u5a608d38"><span data-lake-id="uf8369e77" id="uf8369e77">​</span><br></p>
  <p data-lake-id="ubc5a1c27" id="ubc5a1c27"><span data-lake-id="uda016199" id="uda016199">好，我来这么告诉你，现在假设这些内存碎片所占用的空间之和是1 M，现在新创建了一个对象大小就是 1 M，但是很遗憾的是，此时内存空间虽然加起来有 1 M，但是并不是连续的，所以也就无法存放这大对象。也就是说这样势必会造成内存空间的浪费，这就是内存碎片的危害。</span></p>
  <p data-lake-id="ud4804809" id="ud4804809"><span data-lake-id="ub633ae6a" id="ub633ae6a">​</span><br></p>
  <blockquote data-lake-id="u2cc7aebe" id="u2cc7aebe">
   <p data-lake-id="u29e3abe3" id="u29e3abe3"><span data-lake-id="u04429ca8" id="u04429ca8">比方说其中的1M空间其实依然是可用的，只不过它只能存放&lt;=1M的对象，但是再出现大小完全一模一样的对象是概率很低的事情，即使出现了也并不一定被刚好分配到这段空间上，所以这1M很大概率会被分配给一个&lt;1M的对象，或许只会被利用999K或者1020K或者任意K，剩下的那一点点就很难再被利用了，这才形成了碎片。</span></p>
  </blockquote>
  <p data-lake-id="u27b3e748" id="u27b3e748"><span data-lake-id="uac3956f0" id="uac3956f0">​</span><br></p>
  <p data-lake-id="u4d33d3e8" id="u4d33d3e8"><img src="https://cdn.nlark.com/yuque/0/2022/png/719664/1670154913334-9eb41eee-0121-451f-b968-47bb74978b4b.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_22%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u72ca4fa7" id="u72ca4fa7"><span data-lake-id="u6047be75" id="u6047be75">​</span><br></p>
  <p data-lake-id="ub2e06f05" id="ub2e06f05"><span data-lake-id="u7bd70aa2" id="u7bd70aa2">这么一说标记-清除就没有优点了吗？优点还是有的：速度快</span></p>
  <p data-lake-id="u4bda736f" id="u4bda736f"><span data-lake-id="u0d236527" id="u0d236527">到此，我们来对标记-清除来做一个简单的优缺点小结：</span></p>
  <ul list="u0833884e">
   <li fid="ue0f20303" data-lake-id="u84ce1f9d" id="u84ce1f9d"><span data-lake-id="u7bbdf3ef" id="u7bbdf3ef">优点</span></li>
  </ul>
  <ul list="u0833884e" data-lake-indent="1">
   <li fid="u0f5c608f" data-lake-id="ufb0c701c" id="ufb0c701c"><span data-lake-id="u981f2406" id="u981f2406">速度快，因为不需要移动和复制对象</span></li>
  </ul>
  <ul list="u0833884e" start="2">
   <li fid="ue0f20303" data-lake-id="u2adaf1d6" id="u2adaf1d6"><span data-lake-id="ub83e6b7e" id="ub83e6b7e">缺点</span></li>
  </ul>
  <ul list="u0833884e" data-lake-indent="1">
   <li fid="ue110ece8" data-lake-id="u5faba857" id="u5faba857"><span data-lake-id="ueecf1b6d" id="ueecf1b6d">会产生内存碎片，造成内存的浪费</span></li>
  </ul>
  <h3 data-lake-id="M2GGm" id="M2GGm"><br></h3>
  <h3 data-lake-id="XcIbr" id="XcIbr"><span data-lake-id="u8dd14033" id="u8dd14033">标记-复制</span></h3>
  <p data-lake-id="u40ccd332" id="u40ccd332"><span data-lake-id="uf87cee09" id="uf87cee09">上面的清除算法真的太差劲了。都不管后来人能不能存放的下，就直接啥也不管的去清除对象。所以升级后就来了复制算法。</span></p>
  <p data-lake-id="u47ee2999" id="u47ee2999"><span data-lake-id="u1bacdd01" id="u1bacdd01">​</span><br></p>
  <p data-lake-id="u531ae130" id="u531ae130"><span data-lake-id="uc54d893e" id="uc54d893e">复制算法的工作原理是这样子的：</span><span data-lake-id="uab0f3af0" id="uab0f3af0">首先将内存划分成两个区域。新创建的对象都放在其中一块内存上面，当快满的时候，就将标记出来的存活的对象复制到另一块内存区域中（注意：这些对象在在复制的时候其内存空间上是严格排序且连续的），这样就腾出来一那一半就又变成了空闲空间了</span><span data-lake-id="udb96b3a4" id="udb96b3a4">。依次循环运行。</span></p>
  <p data-lake-id="ubfef3796" id="ubfef3796"><img src="https://cdn.nlark.com/yuque/0/2022/png/719664/1670154913319-524e06c8-3a62-4cd1-bb50-6e0cdb362534.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_27%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u9d126a94" id="u9d126a94"><span data-lake-id="u4059a2ef" id="u4059a2ef">在回收前将存活的对象复制到另一边去。然后再回收垃圾对象，回收完就类似下面的样子：</span></p>
  <p data-lake-id="u1d0d142e" id="u1d0d142e"><img src="https://cdn.nlark.com/yuque/0/2022/png/719664/1670154913266-620d8b08-3141-4fad-adaf-9d68dd6c6b54.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_26%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u13b45887" id="u13b45887"><span data-lake-id="u3a08959d" id="u3a08959d">如果再来新对象被创建就会放在右边那块内存中，当内存满了，继续将存活对象复制到左边，然后清除掉垃圾对象。</span></p>
  <p data-lake-id="u2169ff33" id="u2169ff33"><span data-lake-id="u94db2f3a" id="u94db2f3a">​</span><br></p>
  <p data-lake-id="u0ddf162a" id="u0ddf162a"><span data-lake-id="u76f26be4" id="u76f26be4">标记-复制算法的明显的缺点就是：浪费了一半的内存，但是优点是不会产生内存碎片。所以我们再做技术的时候经常会走向一个矛盾点地方，那就是：</span><span data-lake-id="u19fc898b" id="u19fc898b">一个新的技术的引入，必然会带来新的问题。</span></p>
  <p data-lake-id="uf3461fa1" id="uf3461fa1"><span data-lake-id="ueae5a557" id="ueae5a557">​</span><br></p>
  <p data-lake-id="u645a580a" id="u645a580a"><span data-lake-id="u8129fcbe" id="u8129fcbe">到这里我们来简单小结下标记-复制算法的优缺点：</span></p>
  <ul list="ua8c08b5f">
   <li fid="u4dc9d89d" data-lake-id="u6ceb6324" id="u6ceb6324"><span data-lake-id="uf07a0578" id="uf07a0578">优点</span></li>
  </ul>
  <ul list="ua8c08b5f" data-lake-indent="1">
   <li fid="ue1eb1361" data-lake-id="u05d651f2" id="u05d651f2"><span data-lake-id="ucfe2a029" id="ucfe2a029">内存空间是连续的，不会产生内存碎片</span></li>
  </ul>
  <ul list="ua8c08b5f" start="2">
   <li fid="u4dc9d89d" data-lake-id="ub2e8407c" id="ub2e8407c"><span data-lake-id="ubef38094" id="ubef38094">缺点</span></li>
  </ul>
  <ul list="ua8c08b5f" data-lake-indent="1">
   <li fid="u0cb80f47" data-lake-id="u2b3cbb34" id="u2b3cbb34"><span data-lake-id="ua177cc25" id="ua177cc25">1、浪费了一半的内存空间</span></li>
   <li fid="u0cb80f47" data-lake-id="u2dd82909" id="u2dd82909"><span data-lake-id="ub9a6077e" id="ub9a6077e">2、复制对象会造成性能和时间上的消耗</span></li>
  </ul>
  <p data-lake-id="u94e8b67b" id="u94e8b67b"><br></p>
  <p data-lake-id="u9cf5a546" id="u9cf5a546"><span data-lake-id="u8f9890b7" id="u8f9890b7">说到底，似乎这两种垃圾回收回收算法都不是很好。而且在解决了原有的问题之后，所带来的新的问题也是无法接受的。所以又有了下面的垃圾回收算法。</span></p>
  <p data-lake-id="uf291c169" id="uf291c169"><span data-lake-id="u0e5f7470" id="u0e5f7470">​</span><br></p>
  <h3 data-lake-id="uPvxl" id="uPvxl"><span data-lake-id="u1123e5a3" id="u1123e5a3">标记-整理</span></h3>
  <p data-lake-id="u197ee6f5" id="u197ee6f5"><span data-lake-id="u41158b5c" id="u41158b5c">标记-整理算法是结合了上面两者的特点进行演化而来的。具体的原理和执行流程是这样子的：我们将其分为2个阶段：</span></p>
  <p data-lake-id="ub86e0f79" id="ub86e0f79"><span data-lake-id="ue9759b7a" id="ue9759b7a">​</span><br></p>
  <p data-lake-id="uc7bb87ac" id="uc7bb87ac"><span data-lake-id="ufa685001" id="ufa685001">第一阶段为</span><span data-lake-id="u10f461cf" id="u10f461cf">标记</span><span data-lake-id="ud358e140" id="ud358e140">；</span></p>
  <p data-lake-id="u734ac854" id="u734ac854"><span data-lake-id="u1bae52e4" id="u1bae52e4">第二阶段为</span><span data-lake-id="u7b95b703" id="u7b95b703">整理</span><span data-lake-id="ue42b062d" id="ue42b062d">；</span></p>
  <p data-lake-id="u8fa8b9d3" id="u8fa8b9d3"><span data-lake-id="u65b08671" id="u65b08671">​</span><br></p>
  <p data-lake-id="uad3c4e12" id="uad3c4e12"><span data-lake-id="u230a7552" id="u230a7552">标记：它的第一个阶段与</span><span data-lake-id="u894df4d4" id="u894df4d4">标记-清除</span><span data-lake-id="u50984aff" id="u50984aff">算法是一模一样的，均是遍历 GC Roots，然后将存活的对象标记。</span></p>
  <p data-lake-id="u73008312" id="u73008312"><span data-lake-id="u1828581d" id="u1828581d">整理：移动所有存活的对象，且按照内存地址次序依次排列，然后将末端内存地址以后的内存全部回收。因此，第二阶段才称为整理阶段。</span></p>
  <p data-lake-id="u24b094af" id="u24b094af"><span data-lake-id="u0e1ef1da" id="u0e1ef1da">​</span><br></p>
  <p data-lake-id="u8bb38a84" id="u8bb38a84"><span data-lake-id="u7f4675bb" id="u7f4675bb">我们是画图说话，下面这张图是垃圾回收前的样子。</span></p>
  <p data-lake-id="u75877fad" id="u75877fad"><img src="https://cdn.nlark.com/yuque/0/2022/png/719664/1670154913285-5bb3244c-5922-45bd-808e-7dacaf484788.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_27%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="ubcff7dcb" id="ubcff7dcb"><span data-lake-id="u803da67c" id="u803da67c">下图图表示的第一阶段：标记出存活对象和垃圾对象</span></p>
  <p data-lake-id="u076d9148" id="u076d9148"><img src="https://cdn.nlark.com/yuque/0/2022/png/719664/1670154913927-fe211ad6-339f-4134-ae76-075109a20d5b.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_27%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="uadc3ce06" id="uadc3ce06"><span data-lake-id="uabd385f8" id="uabd385f8">白色空间表示被清理后的垃圾。</span></p>
  <p data-lake-id="uaf3e419e" id="uaf3e419e"><span data-lake-id="ucce558db" id="ucce558db">下面就开始进行整理：</span></p>
  <p data-lake-id="uc894fdff" id="uc894fdff"><img src="https://cdn.nlark.com/yuque/0/2022/png/719664/1670154914105-c7c88dc7-d51d-4e55-9a0a-949e9e6dc190.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_26%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u20ba1f2d" id="u20ba1f2d"><span data-lake-id="u98c074a2" id="u98c074a2">可以看到，现在即没有内存碎片，也没有浪费内存空间。</span></p>
  <p data-lake-id="uf267b4a3" id="uf267b4a3"><span data-lake-id="u3de3dde4" id="u3de3dde4">​</span><br></p>
  <p data-lake-id="u4c80c08a" id="u4c80c08a"><span data-lake-id="u02bff8c3" id="u02bff8c3">但是这就完美了吗？他在标记和整理的时候会消耗大量的时间（微观上）。但是在大厂那种高并发的场景下，这似乎有点不尽如人意。</span></p>
  <p data-lake-id="u70a63d38" id="u70a63d38"><span data-lake-id="u3ec8625f" id="u3ec8625f">​</span><br></p>
  <p data-lake-id="u89e3d6ef" id="u89e3d6ef"><span data-lake-id="u2635aea3" id="u2635aea3">到此，我们将标记-整理的优缺点整理如下：</span></p>
  <ul list="u243d4b5d">
   <li fid="uef2e6985" data-lake-id="u2b03ad8d" id="u2b03ad8d"><span data-lake-id="u2f284c58" id="u2f284c58">优点</span></li>
  </ul>
  <ul list="u243d4b5d" data-lake-indent="1">
   <li fid="u114db300" data-lake-id="ua3ea0ff8" id="ua3ea0ff8"><span data-lake-id="u8a8fa9c5" id="u8a8fa9c5">1、不会产生内存碎片</span></li>
   <li fid="u114db300" data-lake-id="u91cc69bb" id="u91cc69bb"><span data-lake-id="u4158d53b" id="u4158d53b">2、不会浪费内存空间</span></li>
  </ul>
  <ul list="u243d4b5d" start="2">
   <li fid="uef2e6985" data-lake-id="u2b67cac1" id="u2b67cac1"><span data-lake-id="u1f92a2e8" id="u1f92a2e8">缺点</span></li>
  </ul>
  <ul list="u243d4b5d" data-lake-indent="1">
   <li fid="ub7794538" data-lake-id="ud78680f1" id="ud78680f1"><span data-lake-id="u38e1e31a" id="u38e1e31a">太耗时间（性能低）</span></li>
  </ul>
  <p data-lake-id="u98d28245" id="u98d28245"><span data-lake-id="ub1f8d41a" id="ub1f8d41a">​</span><br></p>
  <p data-lake-id="u4f747440" id="u4f747440"><span data-lake-id="u71634eba" id="u71634eba">到此为止，我们已经了知道了标记-清除、标记-复制、标记-整理三大垃圾回收算法的优缺点。</span></p>
  <p data-lake-id="u631b5781" id="u631b5781"><span data-lake-id="ue477ef5a" id="ue477ef5a">​</span><br></p>
  <p data-lake-id="u5834272f" id="u5834272f"><strong><span data-lake-id="uba1c4323" id="uba1c4323">单纯的从时间长短上面来看：标记-清除 &lt; 标记-复制 &lt; 标记-整理。</span></strong></p>
  <p data-lake-id="u203654ee" id="u203654ee"><strong><span data-lake-id="u6f7744e9" id="u6f7744e9">​</span></strong><br></p>
  <p data-lake-id="ue40238ce" id="ue40238ce"><strong><span data-lake-id="u1fee0524" id="u1fee0524">单纯从结果来看：标记-整理 &gt; 标记-复制 &gt;= 标记-清除</span></strong></p>
  <p data-lake-id="ua2f24aa0" id="ua2f24aa0"><br></p>
  <p data-lake-id="u42aa4384" id="u42aa4384"><br></p>
  <p data-lake-id="ubcc1660c" id="ubcc1660c"><br></p>
 </body>
</html>